from os import remove
from time import time
from pathlib import Path
from video_compressor import EXECUTABLE
from video_compressor import LOGLEVEL
from video_compressor import HWACCEL
from video_compressor import THREADS
from video_compressor import C_V
from video_compressor import B_V
from video_compressor import MINRATE
from video_compressor import BUFSIZE
from video_compressor import C_A
from video_compressor import B_A
from video_compressor import AR
from video_compressor import RC
from video_compressor import PRESET
from video_compressor import MOVFLAGS
from video_compressor import TRANSITION
from video_compressor import printf
from video_compressor import Colors
from ffmpy import FFmpeg


global_options = ['-loglevel', LOGLEVEL, '-y', '-hwaccel' if HWACCEL else '', HWACCEL, '-threads', THREADS]


def ffmpy_gpu_video_slice(video_path: Path, out_path: Path, start, end, wide: int, high: int) -> FFmpeg:
    if not video_path.is_file(): return
    video, out = video_path.as_posix(), out_path.as_posix()
    inputs = {video: ['-ss', str(start)]}
    filter_complex = [
        f'[0:v]trim=start=0:end={end - start},setpts=PTS-STARTPTS[vid_trim];',
        f'[vid_trim]scale=w={wide}:h={high}:force_original_aspect_ratio=decrease,pad={wide}:{high}:(ow-iw)/2:(oh-ih)/2:black,setpts=PTS-STARTPTS[video_scale];',
        '[video_scale]format=yuv420p,hwupload_cuda[full_out];',
        '[full_out]hwdownload,format=yuv420p[full_out_video];',
        f'[0:a]aresample=async=1,atrim=start=0:end={end - start}[a_fix];',
        f'[a_fix]aresample=osr={AR},aformat=channel_layouts=stereo,asetpts=PTS-STARTPTS[audio_delayed];',
    ]
    filter_arg = ['-filter_complex', ''.join(filter_complex)]
    out_arg = [
        '-map', '[full_out_video]', '-c:v', C_V, '-b:v', B_V, '-minrate', MINRATE, '-bufsize', BUFSIZE, '-rc', RC, '-preset', PRESET,
        '-map', '[audio_delayed]', '-c:a', C_A, '-b:a', B_A, '-movflags', MOVFLAGS
    ]
    outputs = {out: filter_arg + out_arg}
    ffmpeg = FFmpeg(executable=EXECUTABLE, inputs=inputs, outputs=outputs, global_options=global_options)
    return ffmpeg


def ffmpeg_compressor(file_path: Path, out: Path, wide: int, high: int) -> FFmpeg:
    if not file_path.is_file(): return
    inputs = {file_path.as_posix(): None}
    filter_complex = [
        f'[0:v]scale=w={wide}:h={high}:force_original_aspect_ratio=decrease,pad={wide}:{high}:(ow-iw)/2:(oh-ih)/2:black,setpts=PTS-STARTPTS[video_scale];'
        '[video_scale]format=yuv420p,hwupload_cuda[full_out];',
        '[full_out]hwdownload,format=yuv420p[full_out_video];',
        f'[0:a]aresample=async=1,aresample=osr={AR},aformat=channel_layouts=stereo[a_fix];',
        '[a_fix]asetpts=PTS-STARTPTS[audio];',
    ]
    filter_arg = ['-filter_complex', ''.join(filter_complex)]
    out_put_arg = [
        '-map', '[full_out_video]', '-c:v', C_V, '-b:v', B_V, '-minrate', MINRATE, '-bufsize', BUFSIZE, '-rc', RC, '-preset', PRESET,
        '-map', '[audio]', '-c:a', C_A, '-b:a', B_A, '-movflags', MOVFLAGS
    ]
    outputs = {out.as_posix(): filter_arg + out_put_arg}
    ffmpeg = FFmpeg(executable=EXECUTABLE, inputs=inputs, outputs=outputs, global_options=global_options)
    return ffmpeg


def ffmpy_gpu_video_splicing(file_list: [Path], out_path: Path, wide: int, high: int) -> FFmpeg:
    if not file_list: return
    inputs = {file.as_posix(): None for file in file_list}
    scale_list, cuda_list, audio_list, vid_list, aid_list = [], [], [], [], []
    for i in range(len(file_list)):
        scale_list.append(f'[{i}:v]scale=w={wide}:h={high}:force_original_aspect_ratio=decrease,pad={wide}:{high}:(ow-iw)/2:(oh-ih)/2:black,setpts=PTS-STARTPTS[video_{i}];')
        cuda_list.append(f'[video_{i}]format=yuv420p,hwupload_cuda[cuda_format_{i}];')
        vid_list.append(f'[cuda_format_{i}]')
        audio_list.append(f'[{i}:a]aresample=osr={AR},aformat=channel_layouts=stereo,asetpts=PTS-STARTPTS[audio_{i}];')
        aid_list.append(f'[audio_{i}]')

    vid_list_ = [f"{''.join(vid_list)}concat=n={len(vid_list)}:v=1:a=0[full_video];"]
    aid_list_ = [f"{''.join(aid_list)}concat=n={len(vid_list)}:v=0:a=1[full_audio];"]
    filter_complex = scale_list + cuda_list + vid_list_ + audio_list + aid_list_
    filter_arg = ['-filter_complex', ''.join(filter_complex)]
    out_arg = [
        '-map', '[full_video]', '-c:v', C_V, '-b:v', B_V, '-minrate', MINRATE, '-bufsize', BUFSIZE, '-rc', RC, '-preset', PRESET,
        '-map', '[full_audio]', '-c:a', C_A, '-b:a', B_A, '-movflags', MOVFLAGS
    ]
    outputs = {out_path.as_posix(): filter_arg + out_arg}
    ffmpeg = FFmpeg(executable=EXECUTABLE, inputs=inputs, outputs=outputs, global_options=global_options)
    return ffmpeg


def ffmpy_gpu_video_xfade(file_list: [(Path, int)], out_path: Path, wide: int, high: int) -> FFmpeg:
    if not file_list: return
    if len(file_list) == 1:
        ffmpeg = ffmpy_gpu_video_slice(file_list[0][0], out_path, file_list[0][1], file_list[0][1]+10, wide, high)
    else:
        inputs = {file.as_posix(): ['-ss', str(start)] for file, start in file_list}
        trim_list, scale_list, trim_audio, audio_list, vid_list, aid_list = [], [], [], [], [], []
        time_end = 10
        offset = time_end - 1
        for i in range(len(file_list)):
            trim_list.append(f'[{i}:v]trim=start=0:end={time_end},format=yuv420p,setpts=PTS-STARTPTS[trim_video_{i}];')
            trim_audio.append(f'[{i}:a]atrim=start=0:end={time_end if i == 0 else offset},aresample=osr={AR},asetpts=PTS-STARTPTS[audio_{i}];',)
            scale_list.append(f'[trim_video_{i}]scale=w={wide}:h={high}:force_original_aspect_ratio=decrease,pad={wide}:{high}:(ow-iw)/2:(oh-ih)/2:black,setpts=PTS-STARTPTS[video_{i}];')
            vid_list.append(f'[video_{i}]')
            aid_list.append(f'[audio_{i}]')

        full_video = f'[full_video_0]'
        vid_list_ = [f"{vid_list[0]}{vid_list[1]}xfade=transition={TRANSITION}:duration=1:offset={offset}{full_video};"]
        for i, vid in enumerate(vid_list[2:], start=1):
            xfade = f"{full_video}{vid}xfade=transition={TRANSITION}:duration=1:offset={(i+1)*offset}[full_video_{i}];"
            full_video = f'[full_video_{i}]'
            vid_list_.append(xfade)

        aid_list_ = [f"{''.join(aid_list)}concat=n={len(vid_list)}:v=0:a=1[full_audio];"]
        filter_complex = trim_list + scale_list + vid_list_ + trim_audio + audio_list + aid_list_
        filter_arg = ['-filter_complex', ''.join(filter_complex)]
        out_arg = [
            '-map', f'{full_video}', '-pix_fmt', 'yuv420p', '-c:v', C_V, '-b:v', B_V, '-minrate', MINRATE,
            '-bufsize', BUFSIZE, '-rc', RC, '-preset', PRESET,
            '-map', '[full_audio]', '-c:a', C_A, '-b:a', B_A, '-movflags', MOVFLAGS
        ]
        outputs = {out_path.as_posix(): filter_arg + out_arg}
        ffmpeg = FFmpeg(executable=EXECUTABLE, inputs=inputs, outputs=outputs, global_options=global_options)
    return ffmpeg


def run(ffmpeg, out_path):
    start_time = time()
    result = None
    try:
        if ffmpeg:
            ffmpeg.run()
            result = True
            printf(f'{Colors.OKGREEN}生成成功{Colors.ENDC}{Colors.OKBLUE}{out_path}{Colors.ENDC} {Colors.OKGREEN}耗时：{time() - start_time}{Colors.ENDC}')
    except Exception as e:
        if out_path.is_file(): remove(out_path)
        printf(f'{Colors.FAIL}生成失败{Colors.ENDC}{Colors.OKBLUE}{out_path}{Colors.ENDC} -> {Colors.FAIL}{repr(e)}{Colors.ENDC}')
    return result


